﻿using ChoMy.Constants;
using ChoMy.Helpers;
using ChoMy.Models;
using ChoMy.Models.Data;
using PagedList;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Dynamic;
using System.Web;
using System.Web.Mvc;

namespace ChoMy.Controllers
{
    public class ProductController : Controller
    {
        private ChoMyContext db = new ChoMyContext();
        private StringHelper strHp = new StringHelper();
        private CategoryHelper cateHp = new CategoryHelper();

        public ProductController()
        {
            ViewBag.AltGlobal = GlobalConstant.AltGlobal();
        }

        public ActionResult Index(string categoryNameId, int? page, int? pageSize, string sortOrder)
        {
            if (String.IsNullOrWhiteSpace(categoryNameId))
            {
                return RedirectToAction("BadRequest", "Error");
            }

            Category category = db.Categories
                .FirstOrDefault(x => x.NameId == categoryNameId
                    && x.Level == 2
                    && !x.IsDelete);

            if(category == null)
            {
                return RedirectToAction("NotFound", "Error");
            }

            ViewBag.Category = category;

            List<PropertyType> propertyTypes = GetPropertyTypeByCategory(category);

            ViewBag.PropertyTypes = propertyTypes;

            List<int> ids = ChildCategory(category, Request["Category"]);

            IQueryable<Product> query = db.Products
                .Where(x => ids.Contains(x.CategoryId)
                    && !x.IsDelete);

            query = BrandFilter(query);

            query = PropertyFilter(query, propertyTypes);

            query = Sort(query, sortOrder);

            int pageNumber = page ?? 1;
            pageSize = pageSize ?? 30;

            return View(query.ToPagedList(pageNumber, (int)pageSize));
        }

        public ActionResult Details(string categoryNameId, string productNameId)
        {
            if (String.IsNullOrWhiteSpace(productNameId))
            {
                return RedirectToAction("BadRequest", "Error");
            }

            Product product = db.Products
                .FirstOrDefault(x => x.NameId == productNameId
                    && !x.IsDelete);

            if (product == null)
            {
                return RedirectToAction("NotFound", "Error");
            }

            // Breadcrumb
            List<Category> cateTree = cateHp.Tree(product.CategoryId);
            ViewBag.TreeCategory = cateTree;

            // Check parameter categoryNameId
            Category cateLv2 = cateTree.FirstOrDefault(x => x.Level == 2);
            if (cateLv2.NameId != categoryNameId)
            {
                return RedirectToActionPermanent("Details", new
                {
                    categoryNameId = cateLv2.NameId,
                    productNameId = product.NameId
                });
            }
                       
            // Category property type
            List<PropertyType> propertyTypes = product.Category.CategoryPropertyTypes
                .Select(x => x.PropertyType)
                .Where(x => x.NameId != "color" 
                    && !x.IsDelete)
                .ToList();
            ViewBag.PropertyTypes = propertyTypes;

            // Style filter
            IEnumerable<Style> styles = ColorRequest(product); // Color property
            styles = PropertyRequest(propertyTypes, styles); // Other properties
            ViewBag.Styles = styles.ToList();

            // Viewed product & count views
            SaveViewedProduct(product);
            
            return View(product);
        }

        public ActionResult Search(string search, int? page, int? pageSize)
        {
            int pageNumber = page ?? 1;
            pageSize = pageSize ?? 30;

            ViewBag.Search = search;

            IQueryable<Product> query = null;

            if (!String.IsNullOrWhiteSpace(search))
            {
                page = 1;

                search = strHp.Unsigned(search);

                query = db.Products
                    .Where(x => (x.Category.NameId + "-" + x.NameId).Contains(search))
                    .OrderByDescending(x => x.Description);
            }

            return View(query.ToPagedList(pageNumber, (int)pageSize));
        }

        public ActionResult _ProductItem(Product product, bool lazyLoad)
        {
            ViewBag.LazyLoad = lazyLoad;

            return PartialView(product);
        }

        public ActionResult _ThumbImage(Product product, int? colorId)
        {
            List<Image> images = product.Images
                .Where(x => x.PropertyId == colorId
                    && !x.IsDelete)
                .OrderByDescending(x => x.Priority)
                .ToList();

            ViewBag.Videos = product.Videos
                .Where(x => x.PropertyId == colorId
                    && !x.IsDelete)
                .OrderByDescending(x => x.Priority)
                .ToList();

            return PartialView(images);
        }

        public ActionResult _ColorPicker(Product product, string categoryNameId, int? colorId)
        {
            if(colorId != null)
            {
                ViewBag.Color = db.Properties.Find(colorId);
            }

            ViewBag.CategoryNameId = categoryNameId;
            ViewBag.ProductNameId = product.NameId;

            List<Property> colors = product.Images
                .Where(x => x.PropertyId != null
                    && !x.IsDelete)
                .GroupBy(x => x.Property)
                .Select(x => x.Key)
                .ToList();

            return PartialView(colors);
        }

        public ActionResult _PropertyPicker(List<Style> styles, PropertyType propertyType, string propertyNameId)
        {
            List<Property> properties = styles
                .SelectMany(x => x.StyleProperties)
                .GroupBy(x => x.Property)
                .Select(x => x.Key)
                .Where(x => x.PropertyType.Id == propertyType.Id
                    && !x.IsDelete)
                .ToList();

            ViewBag.PropertyType = propertyType;
            ViewBag.Properties = properties;
            ViewBag.PropertyNameId = propertyNameId;

            return PartialView(new DDLViewModel());    
        }

        public ActionResult _PriceByStyle(List<Style> styles, float? listPrice)
        {
            ViewBag.ListPrice = listPrice;

            return PartialView(styles);
        }

        public ActionResult _Carousel(string type, Product product, string categoryNameId)
        {
            List<Product> products = new List<Product>();

            ViewBag.CategoryNameId = categoryNameId;

            if (type == "viewed")
            {
                ViewBag.CarouselTitle = "Sản phẩm đã xem";
                ViewBag.CarouselId = "viewedCarousel";

                List<int> ids = Session["ViewedProduct"] as List<int>;

                products = db.Products.Where(x => ids.Contains(x.Id)).ToList();
            }
            else
            {
                ViewBag.CarouselTitle = "Sản phẩm cùng loại";
                ViewBag.CarouselId = "sameCarousel";

                products = db.Products
                    .Where(x => x.CategoryId == product.CategoryId)
                    .OrderBy(c => Guid.NewGuid())
                    .Take(12)
                    .ToList();
            }

            return PartialView(products);
        }

        public ActionResult _CategoryFilter(Category category, Category currentCategory)
        {
            List<Category> lstCateLv2 = db.Categories
                .Where(x => x.ParentId == category.ParentId
                    && !x.IsDelete)
                .ToList();

            List<Category> childs = cateHp.Child(category.Id);

            lstCateLv2.AddRange(childs);

            ViewBag.CurrentCategory = currentCategory;
            ViewBag.Category = category;

            return PartialView(lstCateLv2);
        }

        public ActionResult _PropertyFilter(List<SelectListItem> list, string title, string nameId)
        {
            ViewBag.Title = title;
            ViewBag.NameId = nameId;

            return PartialView(list);
        }

        #region Helpers
        private IQueryable<Product> Sort(IQueryable<Product> query, string sortOrder)
        {
            sortOrder = sortOrder ?? "DateCreated_desc";

            ViewBag.SortOrder = sortOrder;

            string tmp = sortOrder.Split('_')[0];

            if(tmp == "ListPrice")
            {
                if (sortOrder.Contains("_desc"))
                {
                    query = query.Where(x => x.Styles.Count() > 0)
                        .OrderByDescending(x => x.Styles.Min(y => y.UnitPrice));
                }
                else
                {
                    query = query.Where(x => x.Styles.Count() > 0)
                        .OrderBy(x => x.Styles.Min(y => y.UnitPrice));
                }
            }
            else
            {
                if (sortOrder.Contains("_desc"))
                {
                    query = query.OrderBy(tmp + " descending");
                }
                else
                {
                    query = query.OrderBy(tmp);
                }
            }

            return query;
        }

        private List<PropertyType> GetPropertyTypeByCategory(Category category)
        {
            var propertyTypes = new List<PropertyType>();

            string rq = Request["Category"] ?? "";

            if (!String.IsNullOrWhiteSpace(rq))
            {
                Category tmpCate = db.Categories
                    .FirstOrDefault(x => x.NameId == rq && !x.IsDelete);

                if (tmpCate != null)
                {
                    propertyTypes = tmpCate.CategoryPropertyTypes.Select(x => x.PropertyType).ToList();
                }
                else
                {
                    propertyTypes = category.CategoryPropertyTypes.Select(x => x.PropertyType).ToList();
                }
            }
            else
            {
                propertyTypes = category.CategoryPropertyTypes.Select(x => x.PropertyType).ToList();
            }

            return propertyTypes;
        }

        private IQueryable<Product> PropertyFilter(IQueryable<Product> query, List<PropertyType> propertyTypes)
        {
            foreach(PropertyType propType in propertyTypes)
            {
                string rq = Request[propType.NameId] ?? "";

                ViewData[propType.NameId] = query
                    .SelectMany(x => x.Styles)
                    .SelectMany(x => x.StyleProperties)
                    .Where(x => x.Property.PropertyTypeId == propType.Id)
                    .GroupBy(x => x.Property)
                    .Select(x => new SelectListItem
                    {
                        Text = x.Key.Name,
                        Value = x.Key.NameId,
                        Selected = rq.Contains(x.Key.NameId)
                    })
                    .OrderBy(x => x.Text)
                    .ToList();

                if (!String.IsNullOrWhiteSpace(rq))
                {
                    query = query
                       .Where(x => x.Styles.Any(y => y.StyleProperties.Any(z =>
                           z.Property.PropertyTypeId == propType.Id && rq.Contains(z.Property.NameId))));
                }
            }

            return query;
        }

        private IQueryable<Product> BrandFilter(IQueryable<Product> query)
        {
            string rq = Request["brand"] ?? "";

            ViewData["brand"] = query.GroupBy(x => x.Brand)
                .Select(x => new SelectListItem
                {
                    Text = x.Key.Name,
                    Value = x.Key.NameId,
                    Selected = rq.Contains(x.Key.NameId)
                })
                .OrderBy(x => x.Text)
                .ToList();

            if(!String.IsNullOrWhiteSpace(rq))
            {
                query = query.Where(x => rq.Contains(x.Brand.NameId));
            }

            return query;
        }

        private List<int> ChildCategory(Category category, string categoryNameId2)
        {
            bool haveRqCategory = false;

            List<Category> childs = new List<Category>();

            if (!String.IsNullOrWhiteSpace(categoryNameId2))
            {
                Category slCategory = db.Categories
                    .FirstOrDefault(x => x.NameId == categoryNameId2
                        && !x.IsDelete);

                if (slCategory != null)
                {
                    ViewBag.CurrentCategory = slCategory;

                    haveRqCategory = true;

                    childs = cateHp.Child(slCategory.Id);
                    childs.Add(slCategory);
                }
            }

            if (haveRqCategory == false)
            {
                ViewBag.CurrentCategory = category;

                childs = cateHp.Child(category.Id);
                childs.Add(category);               
            }

            return childs.Select(x => x.Id).ToList();
        }

        private void SaveViewedProduct(Product product)
        {
            if(Session["ViewedProduct"] == null)
            {
                Session["ViewedProduct"] = new List<int>();
            }

            List<int> ids = Session["ViewedProduct"] as List<int>;

            bool exists = false;

            foreach(var id in ids)
            {
                if (id == product.Id)
                {
                    exists = true;
                    break;
                }
            }

            if(exists == false)
            {
                ids.Insert(0, product.Id);
                Session["ViewedProduct"] = ids;

                // Count views
                product.Views = product.Views + 1;
                db.Entry(product).State = EntityState.Modified;
                db.SaveChanges();
            }
        }

        private IEnumerable<Style> PropertyRequest(List<PropertyType> propertyTypes, IEnumerable<Style> styles)
        {
            foreach(PropertyType propType in propertyTypes)
            {
                string name = propType.Name.Replace(" ", "");

                string request = Request[name];

                ViewData[name] = styles.ToList();

                if(!String.IsNullOrWhiteSpace(request))
                {
                    styles = styles
                        .Where(x => x.StyleProperties.Any(y => y.Property.PropertyTypeId == propType.Id 
                            && y.Property.NameId == request));
                }
            }

            return styles;
        }

        private IEnumerable<Style> ColorRequest(Product product)
        {
            IEnumerable<Style> styles = product.Styles;

            if (!String.IsNullOrWhiteSpace(Request["Color"]))
            {
                string color = Request["Color"] as String;

                Property property = db.Properties
                    .FirstOrDefault(x => x.PropertyType.NameId == "color"
                        && x.NameId == color);

                if (property != null)
                {
                    ViewBag.ColorId = property.Id;

                    styles = styles.Where(x => x.StyleProperties.Any(y => y.PropertyId == property.Id));
                }
            }
            else
            {
                Image image = product.Images.FirstOrDefault();

                if (image != null)
                {
                    int? id = image.PropertyId;

                    ViewBag.ColorId = id;
                }
            }

            return styles;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }

            base.Dispose(disposing);
        }
        #endregion
    }
}